package com.example.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.example.annotation.BlakList;
import com.example.entity.SecKillOrder;
import com.example.entity.SecKillSku;
import com.example.entity.SecKillUser;
import com.example.service.OrderService;
import com.example.service.SkuService;
import com.example.vo.ResponseVO;
import com.example.vo.SecKillMessageVO;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author 游家纨绔
 * @Description TODO
 * @Create 2023-09-05 20:30
 */
@RestController
@RequestMapping("/secKill")
public class SecKillController {

	private final Map<Integer, Boolean> emptyStockMap = new HashMap<>();

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	@Autowired
	private DefaultRedisScript<Long> defaultRedisScript;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private OrderService orderService;
	@Autowired
	private SkuService skuService;

	@PostMapping("/secKillGoods")
	@BlakList(value = "secondKill", limit = 5)
	public ResponseVO secKillGoods(@RequestBody Map<String, Map<String, Object>> map) {
		// 不通过前端
		SecKillUser user = JSONObject.parseObject(JSON.toJSONString(map.get("user")), SecKillUser.class);
		SecKillSku sku = JSONObject.parseObject(JSON.toJSONString(map.get("sku")), SecKillSku.class);
		// 先判断商品是否存在（过期）
		if (!redisTemplate.hasKey(sku.getId().toString())) {
			// 商品过期则在数据库中重新查询放入缓存中
			SecKillSku secKillSku = skuService.selectById(sku.getId());
			redisTemplate.opsForValue().set(secKillSku.getId().toString(), Long.valueOf(secKillSku.getStock()), 3, TimeUnit.SECONDS);
			emptyStockMap.put(secKillSku.getId(), false);
		}
		// 获取订单标识 判断是否重复抢购
		String tag = MessageFormat.format("userId:{0}:{1}", user.getId(), sku.getId());
		if (redisTemplate.hasKey(tag) || user.getId() == 0) {
			return ResponseVO.success(600, "活动太火爆了，请稍后参与");
		}
		// 使用内存标记提前判断减少redis访问，提高性能（库存标记）
		if (emptyStockMap.containsKey(sku.getId()) && emptyStockMap.get(sku.getId())) {
			return ResponseVO.success(700, "秒杀结束");
		}
		// 通过Lua脚本的方式，原子性的去实现库存预减   注意：返回值需得用Long类型
		long stock = redisTemplate.execute(defaultRedisScript, Collections.singletonList("" + sku.getId()), tag);
		// 库存预减到0时，给内存标记打上印记
		if (stock < 0) {
			emptyStockMap.put(sku.getId(), true);
			return ResponseVO.success(800, "秒杀结束");
		}
		if (stock == -2L) {
			return ResponseVO.success(900, "已参与秒杀");
		}
		// 包装用户与秒杀的商品信息
		SecKillMessageVO messageVo = new SecKillMessageVO(user.getId(), sku.getSpuId(), sku.getId());
		// 设置消息的唯一标识Id
		CorrelationData data = new CorrelationData(String.valueOf(user.getId()));
		// mq发送消息
		rabbitTemplate.convertAndSend("secKillExchange", "secKillKey", JSON.toJSONString(messageVo), data);
		return ResponseVO.success(200, "发送成功");
	}

	// 获取秒杀结果
	@RequestMapping("/getResult")
	public Integer result(Integer userId, Integer spuId, Integer skuId) {
		if (Objects.isNull(userId) || Objects.isNull(spuId) || Objects.isNull(skuId)) {
			return 0;
		}
		SecKillOrder commoditySecKillOrder = orderService.selectByUserIdAndSpuIdAndSkuId(userId, spuId, skuId);
		if (Objects.isNull(commoditySecKillOrder)) {
			return 0;
		}
		return 1;
	}

}
